Skip to content

DOC-6909 Add link render hook to replace relref (investigation) - #3732

Open
andy-stark-redis wants to merge 19 commits into
mainfrom
DOC-6909-investigate-alternatives-to-hugo-shortcodes
Open

DOC-6909 Add link render hook to replace relref (investigation)#3732
andy-stark-redis wants to merge 19 commits into
mainfrom
DOC-6909-investigate-alternatives-to-hugo-shortcodes

Conversation

@andy-stark-redis

@andy-stark-redis andy-stark-redis commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Note: implementing this revealed some existing problems that needed fixing before adding the new render hook stuff. The "interesting" stuff (if you can call it that) is in the redis-py client-specific section. It should look and behave exactly as it does currently, but the links are relative to the current page and the notes/warnings are handled with a Github-flavour blockquote format.

You can get a better impression of the benefit of this from the Github code view for the branch. Links in the redis-py section now work in the Github page when you click them and the notes/warnings are also formatted nicely

What this is

Investigation (DOC-6909) into replacing Hugo shortcodes with render hooks to make the Markdown sources more portable. This first step delivers a link render hook to replace the relref shortcode (~30k calls across ~3,500 files), plus the empirical findings that back the approach.

This is a prototype + assessment, not a migration — no content is converted here. It adds:

  • layouts/_default/_markup/render-link.html — the hook.
  • HUGO_DEPENDENCY_ASSESSMENT.md — the broad shortcode-portability assessment, with a new Prototype findings section under Links.

How it was tested

Added the hook, converted every relref in content/develop/clients/ (1,072 calls / 110 files) to plain Markdown links, built the full site, and diffed rendered link targets against a relref baseline.

Internal-link parity is exact across 125 client pages — anchors, mixed-case paths, bare page-relative paths, and .md suffixes all resolve byte-for-byte identically to relref. The only residual differences are cosmetic percent-encoding of literal parentheses in external URLs ((%28).

Key findings

  • relref and plain links can coexist — no atomic migration required. While a relref remains, its destination is still Hugo's shortcode placeholder at hook time (goldmark runs before shortcode substitution), so the hook passes placeholders through untouched. A transition guard suppresses the ~26.8k spurious "unresolved" warnings this would otherwise produce; remove it once relref is fully migrated.
  • Installing the hook is the atomic event, not the content conversion. The hook is global and reprocesses every plain link already in the repo (e.g. the relative reply-type links in command pages), applying benign normalisation (relative→absolute, .md stripped, trailing slash). So the hook must be parity-tested site-wide when it lands; content can then be migrated gradually.
  • A hook must be as robust as Hugo's built-in renderer. Testing surfaced four defects that only appear at corpus scale (each silently dropped pages or failed the build): .Page.File nil-panics on generated/markdownified pages; urls.Parse hard-errors on a pre-existing malformed link and fails the whole build; a fallback double-appended the anchor; and GetPage can't resolve alias targets (false warnings). All are documented and handled/avoided in the committed hook.

Suggested rollout

  1. Land the hook (with the transition guard) and parity-test the whole site against a relref baseline.
  2. Fix pre-existing malformed links first — a hook turns silently-wrong output into hard build failures.
  3. Migrate relref → plain links gradually, section by section.
  4. When the last relref is gone, remove the placeholder guard and optionally make unresolved links a hard error.

🤖 Generated with Claude Code


Note

Medium Risk
The global link hook affects every Markdown link at build time; regressions could break internal navigation or fail builds on malformed URLs, though the pilot claims byte-identical parity to relref.

Overview
Adds Hugo render hooks and a portability assessment so internal docs can move off relref and callout shortcodes toward plain Markdown, without changing the published site when parity is maintained.

Rendering: New render-link.html resolves plain Markdown links (including /content/... repo paths) to permalinks like relref, with a transition guard for un-migrated shortcode placeholders, bundle-resource links, and corpus-scale edge cases documented in the assessment. New render-blockquote.html maps GitHub-style > [!NOTE] / > [!WARNING] alerts to the existing alert UI. process-markdown-content.html now rewrites migrated /content/... links to absolute redis.io URLs for AI/Markdown export.

Content pilot: The redis-py client docs are converted end-to-end—hundreds of relref links to /content/... paths and {{< note >}} blocks to blockquote alerts—plus smaller link fixes (command reference footnotes, RedisVL, operate/security, REST API permission anchor typos, Flex Auto Tiering paths).

Docs: HUGO_DEPENDENCY_ASSESSMENT.md inventories shortcode usage, records link-hook parity testing, and outlines a phased migration plan.

Reviewed by Cursor Bugbot for commit f351060. Bugbot is set up for automated code reviews on this repo. Configure here.

…dings

Adds a prototype link render hook that resolves internal Markdown links to
their permalink, so [text](/path) can replace [text]({{< relref "/path" >}}),
plus the empirical findings in the dependency assessment. Internal-link parity
against a relref baseline is exact across the clients section.

The headline surprise overturned my first conclusion. relref and plain links
do NOT need an atomic migration to coexist. While a relref remains, its link
destination is still Hugo's shortcode placeholder at render-hook time (goldmark
runs before shortcode substitution), so the hook must pass placeholders through
untouched and let Hugo fill in the URL afterwards. The real atomic event is
installing the hook, not converting content, because the hook is global and
reprocesses every plain link already in the repo (the command reply-type links
are relative Markdown, not relref) with benign normalisation. Four defects only
showed up at corpus scale, each silently dropping pages or failing the build,
and are recorded in the assessment so the next attempt does not re-burn them.

Learned: coexistence via placeholder passthrough; hook-install is the atomic step; four corpus-scale hook bugs
Constraint: never dereference .Page.File in a render hook (nil on markdownify-generated and shortcode-inner pages); use .Page.Path
Rejected: urls.Parse for external-link detection | hard-errors on malformed destinations and fails the whole build, use a findRE scheme match
Directive: parity-test the hook against the whole site before migrating any content, then convert relref gradually; remove the HAHAHUGOSHORTCODE guard once no relref remains
Recheck: on Hugo upgrade, confirm the HAHAHUGOSHORTCODE placeholder marker still exists
Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

DOC-6909

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at f351060

Comment thread layouts/_default/_markup/render-link.html Outdated
The first hook only tried .Page.GetPage, so links to companion source files
in the use-case demos ([source](cache.rs) and friends) were reported as
unresolved even though the files exist and the output was correct. These are
page resources, not content pages. The hook now falls back to
.Page.Resources.GetMatch before warning, which drops the false positives
(build warnings 67 -> 40) and normalises the links to their permalink. The
warnings that remain are genuine: real dead links, alias/redirect targets that
GetPage cannot see, and static-directory files.

Learned: internal links include page-bundle resources, not just pages; resolve them with .Page.Resources.GetMatch
Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at 03de590

Links written with an explicit same-directory prefix, e.g.
[bicycle dataset](./data/bicycles.txt), were reported as unresolved because
neither GetPage nor Resources.GetMatch matches a `./`-prefixed path. Strip a
single leading `./` before resolving; `../` is left untouched so GetPage can
still resolve it relative to the current page. bicycles.txt now resolves to its
permalink and stays published; build warnings drop 40 -> 39.

The one that remains here is a genuine content bug the hook surfaces:
./data/products.txt linked from aggregations-syntax.md does not exist anywhere
in content/ (a silent 404 before the hook).

Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread layouts/_default/_markup/render-link.html Outdated
@github-actions

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at 96cd672

@paoloredis

paoloredis commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Before adopting this solution we need to make sure that any tooling that uses the relref construct as a literal string is updated, e.g.

pattern = (
r'(\(\{\{< ?relref "/'
+ self.prefix
+ "/"
+ re.escape(product)
+ r'/([^"]+)" ?>\}\})'
)

andy-stark-redis and others added 2 commits July 30, 2026 14:43
Applies two low-severity review points from Bugbot. Resolution now uses
.PageInner rather than .Page so relative links resolve against the page whose
Markdown contains them under transclusion, and fragment-only (#anchor) links
get their own branch instead of being grouped with external links, matching the
three-category model in the assessment. Both are behaviour-preserving: output
is byte-identical to the previous build and the warning set is unchanged.

Verified .PageInner changes nothing observable in this repo: embed-md
transcludes via .Content (rendered in the target's own context), not
.RenderShortcodes, so .PageInner equals .Page here, and the markdownify-context
warnings still attribute to the calling page. Kept as canonical, future-proof
practice for when .RenderShortcodes transclusion is introduced.

Learned: .PageInner only diverges from .Page under .RenderShortcodes transclusion; this repo's embed-md uses .Content so PageInner is defensive here, not a live fix
Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records the build and authoring tools that parse or generate relref as a
literal string, so the migration plan accounts for them rather than silently
breaking them: version_archiver.py (rewrites relrefs to version links),
redisvl_docs_sync.py (emits relref in an importer), the check_shortcode_paths.py
edit hook, and the Markdown/JSON output partial. Raised by paoloredis on the PR.

Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at 7d340dc

@andy-stark-redis

andy-stark-redis commented Jul 30, 2026

Copy link
Copy Markdown
Contributor Author

Edit: Claude wrote this, btw :-)

Good catch, thanks @paoloredis — captured in the assessment (7d340dc).

I audited build/, layouts/, and .claude/ for relref used as a literal string and found four tools that would need updating or retiring during a migration:

  • build/version_archiver.py (your example) — rewrites {{< relref "/…" >}} to version-specific links; would match nothing against plain Markdown.
  • build/redisvl_docs_sync.pyemits relref in the sphinx → Hugo importer.
  • .claude/hooks/check_shortcode_paths.py — validates relref target paths on edit.
  • layouts/partials/process-markdown-content.html — regex-replaces relref for the Markdown/JSON output.

To be clear on scope: this PR is investigation-only — it adds the render hook and the assessment but converts no content, so none of these tools break yet. The migration plan now lists them explicitly so they're handled before any conversion.

Documents a broader parity run: 825 files and ~5,000 relref calls converted
across four structurally different sections at once (the Active-Active mount,
Redis Cloud, Kubernetes, and integrate). No build errors, no dropped pages, no
new warnings, and every rendered-link difference was benign normalisation.
Notes the Active-Active mount resolves each relative link to the correct
per-mount permalink via .PageInner, and flags that versioned trees were not yet
covered.

Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at c4e3953

andy-stark-redis and others added 2 commits July 30, 2026 15:07
Found by the versioned-tree stress test on a malformed link whose anchor
embedded a full URL with its own `#`. Two defects: the resolved-link branches
composed `RelPermalink + anchor` without safeURL, so Go's html/template
autoescaper distrusted the embedded protocol and blanked the href to ZgotmplZ;
and splitting the anchor on every `#` dropped everything after the second one.
Now the resolved href is piped through safeURL, and the anchor is split on the
first `#` only (via split/after/delimit) so embedded `#` survives intact.
Verified: operate/rs/7.8 (326 files, all with url: overrides) reaches exact
parity with the relref baseline bar the known cosmetic external-paren encoding,
no dropped pages, normal anchors unchanged.

A dead end en route: strings.Index does not exist in Hugo's template namespace
(it silently resolves to nothing and the hook fails on every page), so the
first-# split is done with split + after + delimit instead.

Learned: Hugo has no strings.Index; resolved-link hrefs need safeURL or Go emits ZgotmplZ for anchors containing an embedded protocol
Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
operate/rs/7.8 (326 files, all with url: front-matter overrides) reaches exact
parity with relref; GetPage honours url overrides identically. Notes the
anchor-handling edge case the pass surfaced and how it was fixed.

Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at f8bfc62

Three genuine link bugs found while auditing render-hook build warnings, fixed
in the current relref/source form and independent of any migration (they also
correct the live site):

- references/rest-api/permissions.md (4 versions): the view_all_nodes_alerts
  entry was missing its `#`, so it linked to a non-existent page rather than the
  in-page anchor.
- operate/rs/8.0/flex/_index.md: the Auto Tiering relref pointed at a malformed
  /operate/rs/8.0/7.22/... path; corrected to /operate/rs/7.22/... (the last
  version with the page - 8.0 replaces Auto Tiering with Flex).
- clients/nodejs/amr.md: a doubled [Authority]([Authority](...)) link, unwrapped
  to a single link.

Verified: the five corresponding warnings (2 REF_NOT_FOUND, 4 render-link) clear
with no new warnings and no dropped pages. Left untouched: working alias links
(not bugs), generated redisvl content (fix belongs in the sync script), and a
missing dataset file.

Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Staging links:
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/commands/discard/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/commands/exec/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/commands/expire/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/commands/multi/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/commands/setbit/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/commands/unwatch/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/commands/watch/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/ai/redisvl/0.14.0/user_guide/sql_to_redis_queries/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/ai/redisvl/0.23.0/user_guide/how_to_guides/mcp_authentication/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/ai/redisvl/0.6.0/user_guide/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/ai/redisvl/0.7.0/user_guide/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/ai/redisvl/user_guide/how_to_guides/mcp_authentication/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/ai/search-and-query/advanced-concepts/aggregations-syntax/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/nodejs/amr/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/amr/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/async/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/connect/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/error-handling/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/failover/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/observability/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/prob/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/produsage/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/queryjson/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/scaniter/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/transpipe/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/vecsearch/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/develop/clients/redis-py/vecsets/
https://redis.io/docs/staging/DOC-6909-investigate-alternatives-to-hugo-shortcodes/operate/oss_and_stack/management/optimization/benchmarks/

@github-actions

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at e5665e0

…hook

Clears all but one of the render-hook build warnings (34 -> 1) so merging the
hook will not introduce a wave of warnings for the team. All fixed in current
source form (no migration to plain links here):

- Alias links canonicalised: /develop/interact/transactions ->
  /develop/using-commands/transactions (discard, exec, multi, unwatch, watch);
  /develop/use/keyspace-notifications -> /develop/pubsub/keyspace-notifications
  (expire).
- setbit: data-types-intro#bitmaps -> data-types#bitmaps.
- benchmarks: legacy /topics/pipelining -> /develop/using-commands/pipelining.
- security: GPG-key link made bundle-relative so it resolves as a resource.
- redisgraph release note: bare redisearch.io given an https scheme.
- redisvl sql_to_redis_queries: reversed [url](docs) link corrected to [docs](url).
- redisvl mcp_authentication (0.23.0 + latest): relative mcp.md link -> relref,
  which resolves in page context (these pages render via markdownify, where a
  relative link resolved against the wrong base).
- redisvl 0.6.0/0.7.0 user guides: removed the dead "Release Guides" section
  (target never imported; the latest version already dropped it).

Remaining and deliberately untouched: ./data/products.txt on aggregations-syntax
- the dataset file does not exist anywhere in the repo, so it needs the asset
added or the link removed, which is a team decision.

Verified: 0 build errors, no dropped pages, no new warnings.

Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at 49b53ca

The note pointed readers to ./data/products.txt for download, but that dataset
file has never existed in the repo, so the link was always broken. Removed the
note (confirmed OK with the team). This clears the last outstanding render-hook
warning: the build is now warning-free (0 render-link, 0 REF_NOT_FOUND, 0
errors, no dropped pages).

Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at ce39f5b

…Markdown

First real section migration piloting the render-hook approach: converts all
147 relref calls across the 14 redis-py pages from [text]({{< relref "/path" >}})
to ordinary [text](/path) Markdown links, resolved by the link render hook.

Rendered output is byte-identical to the relref baseline - 0 href differences
across the whole redis-py tree, 0 new build warnings, 0 errors, no dropped
pages. That includes the two bare-relative links
(develop/using-commands/transactions with no leading slash), which GetPage
resolves via the same site-wide fallback as relref.

Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at c7dd76f

Comment thread content/develop/clients/redis-py/transpipe.md Outdated
Adds layouts/_default/_markup/render-blockquote.html so GitHub-style alert
blockquotes (`> [!NOTE]`, `> [!WARNING]`, `> [!TIP]`, `> [!INFO]`, ...) render
with the same styling as the note/warning/tip/info/alert shortcodes, and
documents it in the assessment. Regular blockquotes keep Hugo's default
rendering (verified byte-identical across 21 sampled pages; site-wide count
unchanged at 70). Also fixes a pre-existing malformed alert in the RedisStack
7.2 release note whose body line lacked the `>` prefix, so it now renders inside
the Warning box that the hook activates.

The point is not cosmetic. A shortcode callout markdownifies its inner content
in a page-less context, so relative links inside it resolve against the site
root and break. A blockquote alert's body is native Markdown rendered in page
context, so its links resolve correctly - proven: an identical relative link
resolved inside `> [!NOTE]` but stayed raw inside `{{< note >}}`. This makes the
callout->blockquote migration a prerequisite for portable source-relative links
inside callouts, which is why it should land before the relative-link migration.

Verified: full build clean (0 errors, 0 warnings, 6484 pages).

Learned: blockquote-alert bodies render in page context (links resolve); shortcode callouts markdownify page-less (relative links break) - so blockquotes must precede relative-link migration
Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at f29556e

… alerts

Pilots the callout->blockquote migration on develop/clients/redis-py: converts
all 8 `{{< note >}}` shortcodes across 5 files to portable `> [!NOTE]` Markdown
alerts, rendered by the new blockquote render hook.

Verified against the shortcode baseline: the 8 alert boxes render with identical
styling, identical body text, and 0 href differences across all 5 pages; full
build clean (0 errors, 0 warnings, 6484 pages). The only rendering delta is
benign `<p>`-wrapping of callout bodies (blockquotes always wrap paragraphs;
markdownify did not always) - no change to visible text, links, or the alert
box itself. redis-py had only note callouts, no custom titles, and no
block-level nested shortcodes, so it is the clean base case.

Converter (reusable, handles note/warning/tip/info): prefixes each inner line
with `> `, blank lines become `>` to preserve paragraph breaks. Links inside the
notes are currently absolute, so they already resolved; the portability payoff
(relative links working inside callouts) lands when the relative-link migration
is redone on this section next.

Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at 3139628

Comment thread layouts/_default/_markup/render-blockquote.html
Completes the redis-py portability pilot: converts all 147 internal links from
root-relative absolute paths to source-relative Markdown links with the `.md`
suffix (e.g. [x](../../reference/protocol-spec.md#resp-versions)), so they
resolve in the repo (VS Code, GitHub) as well as via the render hook. Also
enhances the link hook to strip a trailing `/_index.md` or `/index.md`, so links
that point at a section or leaf-bundle file resolve.

This depended on the callout->blockquote migration landed for redis-py: the six
links that live inside callouts only resolve now because those callouts are
blockquote alerts (rendered in page context) rather than markdownify shortcodes.
With that in place the whole section is portable Markdown - relative links in
both body and callouts - and the rendered site is byte-identical to the previous
absolute-link version (0 href diffs across all 14 pages, 0 warnings, 0 errors).
Every relative target was verified to point at a real repo file.

Learned: source-relative .md links + blockquote callouts together give a fully repo-navigable section with identical published output; blockquotes had to land first or the in-callout links break
Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at 2422cff

…itten)

Backs out the five RedisVL page edits from the broken-link cleanup
(sql_to_redis_queries reversed-link, two mcp_authentication relative-link fixes,
and the dead "Release Guides" section removal in 0.6.0/0.7.0). paoloredis
clarified that the RedisVL docs under content/develop/ai/redisvl/ are pulled and
converted from an external site on every sync - even old versions are
refreshed - so hand-edits here are overwritten within a week or so. The real fix
for those links belongs in the import/convert process (build/redisvl_docs_sync.py),
not in the generated pages.

Reverted to the merge-base version so this branch carries no RedisVL changes at
all. This reintroduces the pre-existing RedisVL link warnings that the render
hook surfaces (mcp.md relative link, the reversed sql_to_redis "docs" link, and
the release_guide/ dead links) - deliberately left as out-of-scope, to be fixed
in the sync tooling. aggregations-syntax.md is NOT RedisVL (hand-maintained
search-and-query page) and keeps its fix.

Directive: do not hand-edit content/develop/ai/redisvl/** - it is regenerated from an external source on every sync (even old versions); fix such issues in build/redisvl_docs_sync.py instead
Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at c1ba272

Reinstates the five RedisVL page fixes reverted in c1ba272, per the decision
to keep the broken-link cleanup (RedisVL included) in this PR. Restores the
sql_to_redis reversed-link fix, the two mcp_authentication relative-link fixes,
and the dead "Release Guides" section removal in 0.6.0/0.7.0 - clearing the 22
RedisVL render-link warnings the revert had reintroduced.

Caveat retained for the record: RedisVL docs under content/develop/ai/redisvl/
are regenerated from an external source on every sync (even old versions), so
these edits will be overwritten within ~a week; the durable fix belongs in
build/redisvl_docs_sync.py. Keeping them here anyway keeps the build clean until
that sync happens.

Ticket: DOC-6909
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🧠 Redis Memory

Found 5 related items from repository history:

Memory updated at 07328c9

@andy-stark-redis
andy-stark-redis requested review from a team and paoloredis August 5, 2026 09:41
@dwdougherty

Copy link
Copy Markdown
Collaborator

The one thing I really don't like about this is that, at times, relrefs are being replaced by links with lots of "../../.." sequences. Here's an example:

> [!NOTE]
> Redis Cloud supports relaxed timeouts *only* (and not pre-handoffs) for SCH if you are using
> either [AWS PrivateLink](../../../operate/rc/security/aws-privatelink.md) or
> [Google Cloud Private Service Connect](../../../operate/rc/security/private-service-connect.md)
> (see [Smart client handoffs](../sch.md#redis-cloud) for more information).
> To use relaxed timeouts with these services, you should set `endpoint_type=EndpointType.NONE`
> when you connect. All other configurations have full support for both relaxed timeouts and pre-handoffs.

This will be a nightmare if we every need to move content around. relrefs are much easier to deal with, and, you even wrote a VS Code extension to make them easier to use.

Replaces the source-relative links from 2422cff with repo-root-relative
ones, after review feedback that cross-section targets were producing long
../../../ chains (three levels on 34 of the 147 links, two or more on 100).
Both GitHub's rendered view and VS Code resolve a leading slash against the
repository root, so /content/operate/... is portable in both while staying
resolvable here; the hook needed one guarded prefix strip because GetPage
expects a content-relative path.

The mount behaviour was the surprise, and it argues for this notation on
more than cosmetics. Probing one page of the doubly-mounted Active-Active
tree with three notations pointing at the same in-mount target, the
repo-root-relative form tracks relref exactly under both mounts (each
leaves the mount and lands on the rs permalink), while the source-relative
form keeps the reader inside the rc mount. So the form this commit adopts
is the faithful relref replacement, and the one it removes was quietly
changing behaviour on that tree.

Two verification routes were burned first and both produced confident
false negatives, because neither rewrites relative links at all: GitHub's
markdown API, and the contents API's HTML media type. The control that
caught it was checking whether a known-good relative link in the same
document survived unrewritten. Only the blob view does the rewriting, and
it confirms repo-root semantics. Separately, a site-wide href diff appeared
to show ~96 changed pages until building the identical tree twice showed 44
pages differing with no input change at all; the redisvl and
operate/rc/changelog families are nondeterministic regardless of this
change, and every apparent difference fell inside them.

Parity is exact: 0 href diffs across 5886 hrefs in redis-py and both
Active-Active mounts, 0 warnings, 0 errors, build time unchanged.

Learned: GitHub and VS Code both resolve a leading slash against the repo/workspace root, so /content/-prefixed links are portable, and they track relref on mounted trees where source-relative links do not
Constraint: Hugo resolves the non-portable forms (/develop/x, /develop/x.md, bare develop/x) to the same correct URL with no warning, so /content/ portability is not build-enforced
Rejected: source-relative ../ links | long ../../../ chains for cross-section targets, and they diverge from relref on the mounted Active-Active tree
Rejected: GitHub markdown API and contents-API HTML media type as verification routes | neither rewrites relative links, so both silently report the leading slash as unresolved
Directive: prefer /content/<path>/_index.md over a trailing-slash directory link, because GitHub renders README.md in a folder listing and not _index.md
Gaps: nothing yet lints internal links that are neither /content/-prefixed nor source-relative; the earlier claim that source-relative links match relref on the Active-Active mount needs re-checking against this probe
Reversibility: clean
Ticket: DOC-6909
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@andy-stark-redis

Copy link
Copy Markdown
Contributor Author

The one thing I really don't like about this is that, at times, relrefs are being replaced by links with lots of "../../.." sequences...
This will be a nightmare if we every need to move content around. relrefs are much easier to deal with...

@dwdougherty Yes, definitely. The render hook supports full Github-style URLs (I've had Claude double-check this to be sure), so you can still write paths like /content/commands/acl-list.md that are relative to the repo root using a leading slash. Two issues with this are:

  • you have to add /content as the first segment of the path
  • the links work in VSCode only if your working folder is the repo folder (I think you are using an enclosing folder with the client repos, etc, as your working folder, so the links won't work, but they're no worse than relrefs in that respect). They work fine on the Github Markdown view, though.

Note that existing relrefs still work OK without /content/.

I've updated the source files to use the repo-relative URLs now and it might be a good idea to standardise on that format. Relative URLs still work for now but we could modify the existing Claude edit hook to spit them out or run a separate check for them. Maybe they're useful in some cases, though? It might lower the bar for contributors if we tolerate them but maybe we could periodically replace them with the equivalent repo-relative URLs.

...you even wrote a VS Code extension to make them easier to use.

I guess URLs will usually be added by AI tools going forward but for manual fixes, VSCode's Copy relative path command on the context menu will give you almost the right path, apart from the leading slash. We can easily add a new VSCode extension to copy/insert the exact right path, though, if you think it would still be useful.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3315a24. Configure here.

Comment thread content/develop/clients/redis-py/_index.md

`GROUPBY` ... `REDUCE` ... `APPLY` ... `GROUPBY` ... `REDUCE`

{{< note >}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated callout content removed

Low Severity

The overview note explaining that the page examples use a hypothetical products dataset (with a download link) was deleted. That change is unrelated to the link/blockquote render-hook work and drops useful page context.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3315a24. Configure here.

Bugbot caught that the migrated redis-py pages shipped raw source paths into
the .md and .json outputs, because the pipeline rewrites relref to absolute
redis.io URLs and nothing else. Adds three rules mirroring the relref one,
so a repo-root-relative link becomes the same absolute published URL. The
section and leaf-bundle forms run first, so the index segment is dropped
rather than left in the URL, and an anchor sits outside each match and
survives. The /index.md rule matches no content today and is there to keep
this in step with the render-link hook, which strips that suffix.

Worth being straight about how this got through. Parity for the notation
change was claimed on the strength of HTML hrefs alone, and the .md/.json
outputs were never opened, which is precisely the trap already recorded in
the notes on this pipeline. It was not a regression introduced by the
notation change, since the source-relative form was equally unresolvable in
the feed, but the earlier parity claim was broader than what had actually
been measured. The generalisation that matters for the rollout is that
relref is the only form the feed rewrites, so every section moved off it
needs these rules in place first.

Scope boundary found while verifying, deliberately not addressed here: 394
.html.md files leak pre-existing ../ relative links into the feed and 21
leak rooted non-/content/ links. Those are the plain links that were already
in the corpus before any of this work, and the hook normalises them in HTML
while the feed never has. Independent, pre-existing, and much wider than
this branch.

Verified on a full rebuild: no build warnings, no remaining ](/content/
destinations in .md or .json, HTML hrefs byte-identical to the pre-fix
build, and three sampled relref-based pages produce identical feed output.
The 110 files still containing the /content/ substring are github.com blob
URLs, correctly untouched because the rules anchor on a link destination.

Learned: relref is the only link form the AI feed pipeline rewrites, so HTML href parity is not evidence of feed parity and the .md/.json output must be opened separately
Constraint: any section migrated off relref needs these /content/ rewrite rules in place, or its Markdown and JSON output ships source paths that resolve nowhere on the published site
Directive: keep these rules in step with the render-link hook's suffix stripping — it strips /_index and /index, so the pipeline must strip both too
Gaps: 394 .html.md files still leak pre-existing ../ relative links and 21 leak rooted non-/content/ links; that defect predates DOC-6909 and is untouched here
Reversibility: clean
Ticket: DOC-6909
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants